package org.spider.download.playwright;

import com.microsoft.playwright.*;
import com.microsoft.playwright.options.Cookie;
import com.microsoft.playwright.options.Proxy;
import lombok.extern.slf4j.Slf4j;
import org.spider.download.Downloader;
import org.spider.download.cache.CacheManager;
import org.spider.model.DownloadRequest;
import org.spider.model.DownloadResponse;
import org.spider.model.PlaywrightManager;
import org.spider.utils.UrlUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.nio.file.Paths;
import java.util.List;

@Slf4j
public class PWChromeDownloader implements Downloader {

    @Override
    public DownloadResponse download(DownloadRequest downloadRequest) {
        String cookieFilePath = "src/main/resources/cookies/"
                + UrlUtils.getDomain(downloadRequest.getUrl()) + ".json";
        BrowserContext browserContext = browserContext(downloadRequest, cookieFilePath);
        Page page = browserContext.newPage();
        DownloadResponse downloadResponse = new DownloadResponse();
        downloadResponse.setDownloadRequest(downloadRequest);
        for (int i = 0; i< downloadRequest.getRetryCount(); i++) {
            try {
                Response res = page.navigate(downloadRequest.getUrl());
                downloadResponse.setStatusCode(res.status());
                downloadResponse.setBytes(res.body());
                downloadResponse.setHeaderMap(res.headers());
                if (!CollectionUtils.isEmpty(downloadRequest.getOperationList()) &&
                        !new File(cookieFilePath).exists()) {
                    downloadRequest.getOperationList().forEach((operation) -> {
//                    operations.forEach((operation -> {
                        switch (operation.getOperationTypeEnum()) {
                            case CLICK:
                                page.click(operation.getExpress());
                                page.waitForSelector(operation.getSuccessExpress());
                                return;
                            case FILL:
                                page.fill(operation.getExpress(), operation.getValue());
                                page.waitForSelector(operation.getSuccessExpress());
                                return;
                            case CHECK:
                                page.check(operation.getExpress());
                                page.waitForSelector(operation.getSuccessExpress());
                                return;
                            case HOVER:
                                page.hover(operation.getExpress());
                                page.waitForSelector(operation.getSuccessExpress());
                                return;
                            case FOCUS:
                                page.focus(operation.getExpress());
                                page.waitForSelector(operation.getSuccessExpress());
                                return;
                            case DBCLICK:
                                page.dblclick(operation.getExpress());
                                page.waitForSelector(operation.getSuccessExpress());
                                return;
                            default:
                                break;
                        }
//                    }));
                    });
                }
                List<Cookie> cookies = page.context().cookies();
                StringBuilder stringBuilder = new StringBuilder();
                for (Cookie cookie : cookies) {
                    stringBuilder.append(cookie.name)
                            .append("=")
                            .append(cookie.value)
                            .append(";");
                }
                downloadResponse.setResCookie(stringBuilder.toString());
                downloadResponse.setRawText(page.content());
                break;
            }catch (Exception e) {
                log.error("下载失败了第几次:{}，downloaderRequest：{}", i, downloadRequest, e);
            }
        }
        browserContext.storageState(new BrowserContext.StorageStateOptions().setPath(Paths.get(cookieFilePath)));
        page.close();
        browserContext.close();
        expiredHandle();
        return downloadResponse;
    }

    private BrowserContext browserContext(DownloadRequest downloadRequest, String cookieFilePath) {
        PlaywrightManager playwrightManager;
        if (CacheManager.playwrightManagerMap.containsKey(Thread.currentThread().getId())) {
            playwrightManager = CacheManager.playwrightManagerMap.get(Thread.currentThread().getId());
        }else {
            Playwright playwright = Playwright.create();
            BrowserType.LaunchOptions launchOptions = new BrowserType.LaunchOptions();
            launchOptions.setProxy("per-context");
            launchOptions.setTimeout(60*1000);
            launchOptions.setHeadless(false);
            Browser browser = playwright.chromium().launch(launchOptions);
            playwrightManager = new PlaywrightManager(playwright, browser);
            CacheManager.playwrightManagerMap.put(Thread.currentThread().getId(), playwrightManager);
        }
        Browser.NewContextOptions newContextOptions = new Browser.NewContextOptions();
        if (downloadRequest.getDownloadProxy() != null) {
            Proxy proxy = new Proxy(downloadRequest.getDownloadProxy().getIp() + ":" + downloadRequest.getDownloadProxy().getPort());
            if (StringUtils.hasText(downloadRequest.getDownloadProxy().getUsername())) {
                proxy.setUsername(downloadRequest.getDownloadProxy().getUsername());
                proxy.setPassword(downloadRequest.getDownloadProxy().getPassword());
            }
            newContextOptions.setProxy(proxy);
        }
        // 更新实例最新使用时间，避免被回收
        expiredUpdate(playwrightManager);
        if (new File(cookieFilePath).exists()) {
            newContextOptions.setStorageStatePath(Paths.get(cookieFilePath));
        }
        return playwrightManager.getBrowser().newContext(newContextOptions);
    }

    private void cloeClient() {
        PlaywrightManager playwrightManager = CacheManager.playwrightManagerMap.get(Thread.currentThread().getId());
        playwrightManager.getBrowser().close();
        playwrightManager.getPlaywright().close();
    }

    public static void expiredUpdate(PlaywrightManager playwrightManager) {
        playwrightManager.setLastUseTime(System.currentTimeMillis());
        CacheManager.playwrightManagerMap.put(Thread.currentThread().getId(), playwrightManager);
    }

    public static void expiredHandle() {
        CacheManager.playwrightManagerMap.forEach((key, value) -> {
            if ((System.currentTimeMillis() - value.getLastUseTime()) > 60*60*1000) {
                CacheManager.playwrightManagerMap.remove(key);
                value.getBrowser().close();
                value.getPlaywright().close();
            }
        });
    }
}
